package com.ji.zhiqu.scoring;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.ji.zhiqu.manager.AiManager;
import com.ji.zhiqu.model.dto.question.QuestionAnswerDTO;
import com.ji.zhiqu.model.dto.question.QuestionContentDTO;
import com.ji.zhiqu.model.entity.App;
import com.ji.zhiqu.model.entity.Question;
import com.ji.zhiqu.model.entity.ScoringResult;
import com.ji.zhiqu.model.entity.UserAnswer;
import com.ji.zhiqu.model.vo.QuestionVO;
import com.ji.zhiqu.service.QuestionService;
import com.ji.zhiqu.service.ScoringResultService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * AI 测评类app的评分策略
 */
@ScoringStrategyConfig(appType = 1, scoringStrategy = 1)
public class AiTestScoringStrategy implements ScoringStrategy {
    @Resource
    private QuestionService questionService;
    @Resource
    private AiManager aiManager;

    // 分布式锁
    @Resource
    private RedissonClient RedissonClient;
    private static final String AI_ANSWER_LOCK = "AI_ANSWER_LOCK";

    // Caffeine本地缓存：AI评价结果 key为：固定前缀 + MD5哈希(appId+选择列表)，value为评价的json
    private final Cache<String, String> answerCacheMap =
            // 创建缓存，初始化容量为1024，泛型设置k-v
            Caffeine.newBuilder().initialCapacity(1024)
                    // 缓存5分钟移除
                    .expireAfterAccess(5L, TimeUnit.MINUTES)
                    .build();


    // AI测评的系统 prompt
    private static final String AI_TEST_SCORING_SYSTEM_MESSAGE = "你是一位严谨的判题专家，我会给你如下信息：\n" +
            "```\n" +
            "应用名称，\n" +
            "【【【应用描述】】】，\n" +
            "题目和用户回答的列表：格式为 [{\"title\": \"题目\",\"answer\": \"用户回答\"}]\n" +
            "```\n" +
            "\n" +
            "请你根据上述信息，按照以下步骤来对用户进行评价：\n" +
            "1. 要求：需要给出一个明确的评价结果，包括评价名称（尽量简短）和评价描述（尽量详细，大于 200 字）\n" +
            "2. 严格按照下面的 json 格式输出评价名称和评价描述\n" +
            "```\n" +
            "{\"resultName\": \"评价名称\", \"resultDesc\": \"评价描述\"}\n" +
            "```\n" +
            "3. 返回格式必须为 JSON 对象";
    @Autowired
    private RedissonClient redissonClient;


    /**
     * 获取AI测评的userMessage, 如：
     应用名称，
     【【【应用描述】】】，
     题目和用户回答的列表：格式为 [{"title": "题目","answer": "用户回答"}]
     * @param app
     * @param questionContentDTOList
     * @param choices
     * @return
     */
    private String getAiTestScoringUserMessage(App app, List<QuestionContentDTO> questionContentDTOList, List<String> choices) {
        // 获取AI测评的userPrompt
        StringBuilder userMessage = new StringBuilder();
        userMessage.append(app.getAppName()).append("\n");
        userMessage.append(app.getAppDesc()).append("\n");
        List<QuestionAnswerDTO> questionAnswerDTOList = new ArrayList<>();
        // 遍历题目和用户选择的选项，将题目和用户选择的选项封装到QuestionAnswerDTO对象中
        for (int i = 0; i < questionContentDTOList.size(); i++) {
            QuestionAnswerDTO questionAnswerDTO = new QuestionAnswerDTO();
            questionAnswerDTO.setTitle(questionContentDTOList.get(i).getTitle());
            // 找出匹配choices key的value
            List<QuestionContentDTO.Option> options = questionContentDTOList.get(i).getOptions();
            for (QuestionContentDTO.Option option : options) {
                if (option.getKey().equals(choices.get(i))) {
                    questionAnswerDTO.setUserAnswer(option.getValue());
                }
            }
            // questionAnswerDTO.setUserAnswer(choices.get(i));
            questionAnswerDTOList.add(questionAnswerDTO);
        }
        // 将题目和用户回答的列表转换为JSON格式
        userMessage.append(JSONUtil.toJsonStr(questionAnswerDTOList));
        return userMessage.toString();
    }

    /**
     * 核心：根据用户选择choices和appId，调用AI获取评测结果
     * @param choices
     * @param app
     * @return
     * @throws Exception
     */
    @Override
    public UserAnswer doScore(List<String> choices, App app) throws Exception {
        // 1. 查询缓存
        // 根据appId查询题目
        Long appId = app.getId();
        String jsonStr = JSONUtil.toJsonStr(choices);
        // 查询缓存
        String cacheKey = buildCacheKey(appId, jsonStr);
        String answerJson = answerCacheMap.getIfPresent(cacheKey);

        // 有缓存，直接返回
        if(StrUtil.isNotBlank(answerJson)){
            // 封装缓存里的AI评价(json格式)
            UserAnswer userAnswer = JSONUtil.toBean(answerJson, UserAnswer.class);
            // 填充其他信息
            userAnswer.setAppId(appId);
            userAnswer.setAppType(app.getAppType());
            userAnswer.setScoringStrategy(app.getScoringStrategy());
            userAnswer.setChoices(jsonStr);
            return userAnswer;
        }

        // 定义锁 (app和答案一样才加锁)
        RLock lock = redissonClient.getLock(AI_ANSWER_LOCK + cacheKey);
        try{
            // 2. 没有缓存，加锁调用AI
            boolean isLock = lock.tryLock(3, 15, TimeUnit.SECONDS);
            if(!isLock){
                return null;
            }

            // 查找题目
            Question question = questionService.getOne(
                    Wrappers.lambdaQuery(Question.class).eq(Question::getAppId, appId)
            );
            // 转为题目列表
            QuestionVO questionVO = QuestionVO.objToVo(question);
            List<QuestionContentDTO> questionContent = questionVO.getQuestionContent();

            // 调用AI获取评测结果
            // 封装 userPrompt
            String userPrompt = getAiTestScoringUserMessage(app, questionContent, choices);
            // 调用AI，得到json结果
            String result = aiManager.doSyncStableRequest(AI_TEST_SCORING_SYSTEM_MESSAGE, userPrompt);
            // 处理json转为结果对象
            int start = result.indexOf("{");
            int end = result.lastIndexOf("}") + 1;
            String json = result.substring(start , end);

            // 缓存结果
            answerCacheMap.put(cacheKey, json);

            // 3. 构造返回值，填充答案对象的属性
            // AI 只生成resultName，resultDesc
            UserAnswer userAnswer = JSONUtil.toBean(json, UserAnswer.class);
            // 填充其他信息
            userAnswer.setAppId(appId);
            userAnswer.setAppType(app.getAppType());
            userAnswer.setScoringStrategy(app.getScoringStrategy());
            userAnswer.setChoices(jsonStr);
            // 不保存评分结果到数据库
            //userAnswer.setResultId(maxScoringResult.getId());
            return userAnswer;
        }finally {
            // 释放锁
            if(lock != null && lock.isLocked()){
                // 本线程释放，防误删
                if(lock.isHeldByCurrentThread()){
                    lock.unlock();
                }
            }
        }
    }



    /**
     * 根据appId和用户选择的json，构建缓存key
     * @param appId
     * @param choices
     * @return
     */
    private String buildCacheKey(Long appId, String choices) {
        // 通过MD5哈希算法生成一个唯一的缓存Key
        return DigestUtil.md5Hex(appId + ":" + choices);
    }
}
